"""
Database base models and configuration following SOLID principles.
"""

from datetime import datetime
from typing import Any, Dict
from sqlalchemy import Column, Integer, DateTime, String, Boolean, Text
from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy.orm import Session
from sqlalchemy.sql import func

# Create base class
Base = declarative_base()


class BaseModel(Base):
    """Base model with common fields and methods."""
    
    __abstract__ = True
    
    id = Column(Integer, primary_key=True, index=True)
    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
    is_active = Column(Boolean, default=True, nullable=False)
    
    @declared_attr
    def __tablename__(cls):
        """Generate table name from class name."""
        return cls.__name__.lower() + 's'
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert model to dictionary."""
        return {
            column.name: getattr(self, column.name)
            for column in self.__table__.columns
        }
    
    def update_from_dict(self, data: Dict[str, Any]) -> None:
        """Update model from dictionary."""
        for key, value in data.items():
            if hasattr(self, key) and key not in ['id', 'created_at']:
                setattr(self, key, value)
    
    def soft_delete(self) -> None:
        """Soft delete the record."""
        self.is_active = False
        self.updated_at = datetime.utcnow()
    
    def restore(self) -> None:
        """Restore soft deleted record."""
        self.is_active = True
        self.updated_at = datetime.utcnow()
    
    @classmethod
    def get_active_query(cls, session: Session):
        """Get query for active records only."""
        return session.query(cls).filter(cls.is_active == True)
    
    def __repr__(self) -> str:
        """String representation of the model."""
        return f"<{self.__class__.__name__}(id={self.id})>"


class TimestampMixin:
    """Mixin for timestamp fields."""
    
    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)


class SoftDeleteMixin:
    """Mixin for soft delete functionality."""
    
    is_active = Column(Boolean, default=True, nullable=False)
    deleted_at = Column(DateTime(timezone=True), nullable=True)
    
    def soft_delete(self) -> None:
        """Soft delete the record."""
        self.is_active = False
        self.deleted_at = datetime.utcnow()
    
    def restore(self) -> None:
        """Restore soft deleted record."""
        self.is_active = True
        self.deleted_at = None


class AuditMixin:
    """Mixin for audit fields."""
    
    created_by = Column(String(255), nullable=True)
    updated_by = Column(String(255), nullable=True)
    
    def set_created_by(self, user_id: str) -> None:
        """Set created by user."""
        self.created_by = user_id
    
    def set_updated_by(self, user_id: str) -> None:
        """Set updated by user."""
        self.updated_by = user_id


class VersionMixin:
    """Mixin for versioning."""
    
    version = Column(Integer, default=1, nullable=False)
    
    def increment_version(self) -> None:
        """Increment version number."""
        self.version += 1


class MetadataMixin:
    """Mixin for metadata fields."""
    
    metadata_json = Column(Text, nullable=True)
    tags = Column(String(1000), nullable=True)
    
    def set_metadata(self, metadata: Dict[str, Any]) -> None:
        """Set metadata as JSON string."""
        import json
        self.metadata_json = json.dumps(metadata)
    
    def get_metadata(self) -> Dict[str, Any]:
        """Get metadata as dictionary."""
        import json
        if self.metadata_json:
            return json.loads(self.metadata_json)
        return {}
    
    def add_tag(self, tag: str) -> None:
        """Add a tag."""
        if self.tags:
            tags = self.tags.split(',')
            if tag not in tags:
                tags.append(tag)
                self.tags = ','.join(tags)
        else:
            self.tags = tag
    
    def remove_tag(self, tag: str) -> None:
        """Remove a tag."""
        if self.tags:
            tags = self.tags.split(',')
            if tag in tags:
                tags.remove(tag)
                self.tags = ','.join(tags) if tags else None
    
    def get_tags(self) -> list:
        """Get list of tags."""
        if self.tags:
            return self.tags.split(',')
        return []


class FullAuditModel(BaseModel, AuditMixin, VersionMixin):
    """Model with full audit capabilities."""
    
    __abstract__ = True
    
    def update_audit_fields(self, user_id: str) -> None:
        """Update audit fields."""
        self.set_updated_by(user_id)
        self.increment_version()
        self.updated_at = datetime.utcnow()


class SimpleModel(Base, TimestampMixin, SoftDeleteMixin):
    """Simple model with basic functionality."""
    
    __abstract__ = True
    
    id = Column(Integer, primary_key=True, index=True)
    
    @declared_attr
    def __tablename__(cls):
        """Generate table name from class name."""
        return cls.__name__.lower() + 's'
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert model to dictionary."""
        return {
            column.name: getattr(self, column.name)
            for column in self.__table__.columns
        }
    
    def __repr__(self) -> str:
        """String representation of the model."""
        return f"<{self.__class__.__name__}(id={self.id})>"